#include "HttpSession.hpp"

#include "HttpServer.hpp"

#include <boost/algorithm/string.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/core/string_type.hpp>
#include <boost/beast/http/file_body.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/websocket.hpp>
#include <iostream>
#include <string/stringsplit.h>
#include <tools.h>

using namespace rdhttp;

using namespace boost::beast;
static void
fail(boost::beast::error_code ec, char const* what) {
    std::cerr << what << ": " << ec.message() << "\n";
}
namespace rdhttp {

    std::string HttpSession::server_name = BOOST_BEAST_VERSION_STRING;

    HttpSession::HttpSession(
        boost::asio::ip::tcp::socket&& socket,
        std::string const& doc_root,
        std::map<std::string, RequestHandler>& get_handlers,
        std::map<std::string, PosttHandler>& post_handlers)
        : m_stream(std::move(socket)), doc_root(doc_root), get_handlers(get_handlers), post_handlers(post_handlers) {
    }

    HttpSession::~HttpSession() {
    }

    void HttpSession::start() {
        run();
    }

    tcp_stream& HttpSession::stream() {
        return m_stream;
    }

    boost::optional<HttpSession::HttpRequestParser>& HttpSession::parser() {
        return m_parser;
    }

    flat_buffer& HttpSession::buffer() {
        return m_buffer;
    }

    // Start the asynchronous operation
    void HttpSession::run() {
        // We need to be executing within a strand to perform async operations
        // on the I/O objects in this HttpSession. Although not strictly necessary
        // for single-threaded contexts, this example code is written to be
        // thread-safe by default.

        m_parser.emplace();
        m_parser->body_limit((std::numeric_limits<std::uint64_t>::max)());
        boost::asio::dispatch(m_stream.get_executor(),
            boost::beast::bind_front_handler(
                &HttpSession::do_read_header,
                shared_from_this()));
    }

    void HttpSession::do_read_header() {
        // Make the request empty before reading,
        // otherwise the operation behavior is undefined.
        // Set the timeout.

        m_stream.expires_after(std::chrono::seconds(30));

        // Read a request header 读请求头
        boost::beast::http::async_read_header(m_stream, m_buffer, *m_parser,
            boost::beast::bind_front_handler(
                &HttpSession::on_read_header,
                shared_from_this()));
    }

    void HttpSession::on_read_header(
        boost::beast::error_code ec,
        std::size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);

        // This means they closed the connection
        if (ec == boost::beast::http::error::end_of_stream) {
            return do_close();
        }
        if (ec == boost::beast::http::error::need_buffer) {
            do_read_header();
            return;
        }

        if (ec == boost::beast::http::error::body_limit) {
            // std::cout << "on_read_header:" << m_parser->get() << std::endl;
            // m_parser.emplace();
            // m_parser->body_limit((std::numeric_limits<std::uint64_t>::max)());
            // do_read_body();
            return;
        }
        if (ec) {
            return fail(ec, "read");
        }
        // See if it is a WebSocket Upgrade
        if (boost::beast::websocket::is_upgrade(m_parser->get())) {
            // Create a websocket session, transferring ownership
            // of both the socket and the HTTP request.
            // std::make_shared<websocket_session>(
            //     stream_.release_socket())
            //     ->do_accept(parser_->release());
            return;
        }

        if (!m_parser->is_done()) {
            do_read_body();
            return;
        }

        read_done();
    }

    void HttpSession::do_read_body() {
        // Read a request body 读请求体
        if (!m_parser->is_done()) {
            boost::beast::http::async_read(m_stream, m_buffer, *m_parser,
                boost::beast::bind_front_handler(
                    &HttpSession::on_read_body,
                    shared_from_this()));

            return;
        }

        read_done();
    }

    void HttpSession::on_read_body(boost::beast::error_code ec, std::size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);

        if (bytes_transferred > 0) {
            string_view buff((char*)m_buffer.data().data(), bytes_transferred);
        }
        // std::cout << "on_read_body:" << m_parser->get() << std::endl;

        // This means they closed the connection
        if (ec == boost::beast::http::error::end_of_stream) {
            return do_close();
        }
        if (ec == boost::beast::http::error::need_buffer) {
            do_read_body();
            return;
        }
        if (ec == boost::beast::http::error::body_limit) {
            return fail(ec, "body_limit");
        }
        if (ec) {
            return fail(ec, "read");
        }
        if (!m_parser->is_done()) {
            do_read_body();
            return;
        }
        read_done();
    }

    void HttpSession::read_done() {
        request.emplace();
        request = m_parser->release();
        // Send the response
        send_response(handle_request());
    }

    void HttpSession::send_response(boost::beast::http::message_generator&& msg) {
        bool keep_alive = msg.keep_alive();

        // Write the response
        boost::beast::async_write(
            m_stream,
            std::move(msg),
            boost::beast::bind_front_handler(
                &HttpSession::on_write, shared_from_this(), keep_alive));
    }

    void HttpSession::on_write(
        bool keep_alive,
        boost::beast::error_code ec,
        std::size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);

        if (ec)
            return fail(ec, "write");

        if (!keep_alive) {
            // This means we should close the connection, usually because
            // the response indicated the "Connection: close" semantic.
            return do_close();
        }

        // Read another request
        m_parser.emplace();
        m_parser->body_limit((std::numeric_limits<std::uint64_t>::max)());
        do_read_header();
    }

    void HttpSession::do_close() {
        // Send a TCP shutdown
        boost::beast::error_code ec;
        m_stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);

        // At this point the connection is closed gracefully
    }

    // Return a response for the given request.
    //
    // The concrete type of the response message (which depends on the
    // request), is type-erased in message_generator.
    boost::beast::http::message_generator
    HttpSession::handle_request() {
        HttpResponse response;
        response.set(boost::beast::http::field::content_type, "text/plain; charset=UTF-8");
        response.result(boost::beast::http::status::ok);
        response.version(request->version());
        response.set(boost::beast::http::field::server, server_name);

        boost::beast::http::response<boost::beast::http::string_body> res1(response);

        boost::urls::url url(request->target());
        std::string path = url.path();

        std::cout << "__________________________________________________________________________"
                  << std::endl
                  << request->method() << " " << path << std::endl;

        // Make sure we can handle the method
        switch (request->method()) {
            case boost::beast::http::verb::get: {
                auto it = get_handlers.find(path);

                if (it != get_handlers.end()) {
                    it->second(request.get(), response);
                    boost::beast::http::response<boost::beast::http::string_body> res(response);
                    res.prepare_payload();
                    return res;
                }
            } break;

            case boost::beast::http::verb::post: {
                auto it = post_handlers.find(path);
                if (it != post_handlers.end()) {
                    it->second(request.get(), response);
                    response.prepare_payload();
                    return boost::beast::http::response<boost::beast::http::string_body>(response);
                }
            } break;

            case boost::beast::http::verb::head: {
            } break;

            default: {
                return bad_request("Unknown HTTP-method");
            } break;
        }

        // Request path must be absolute and not contain "..".
        // 请求路径必须是绝对路径，且不包含 ".."
        if (path.empty() ||
            path[0] != '/' ||
            path.find("..") != boost::beast::string_view::npos)
            return bad_request("Illegal request-target");

        // Build the path to the requested file
        // 构建请求文件的路径
        std::string filepath = path_cat(doc_root, path);
        if (filepath.back() == '/')
            filepath.append("index.html");

        // Attempt to open the file
        // 尝试打开文件
        boost::beast::error_code ec;
        boost::beast::http::file_body::value_type body;

        std::cout << "get:" << filepath << std::endl;
        body.open(filepath.c_str(), boost::beast::file_mode::scan, ec);

        // Handle the case where the file doesn't exist
        // 处理文件不存在的情况
        if (ec == boost::beast::errc::no_such_file_or_directory)
            return not_found(filepath);

        // Handle an unknown error
        if (ec)
            return server_error(ec.message());

        // Cache the size since we need it after the move
        auto const size = body.size();

        // Respond to HEAD 响应 HEAD 请求
        if (request->method() == boost::beast::http::verb::head) {
            boost::beast::http::response<boost::beast::http::string_body> res{boost::beast::http::status::ok, request->version()};
            res.set(boost::beast::http::field::server, server_name);
            res.set(boost::beast::http::field::content_type, std::string(mime_type(filepath)) + "; charset=UTF-8");
            res.content_length(size);
            res.keep_alive(request->keep_alive());
            return res;
        }

        // Respond to GET 响应 GET 请求
        boost::beast::http::response<boost::beast::http::file_body> res{
            std::piecewise_construct,
            std::make_tuple(std::move(body)),
            std::make_tuple(boost::beast::http::status::ok, request->version())};
        res.set(boost::beast::http::field::server, server_name);
        res.set(boost::beast::http::field::content_type, std::string(mime_type(filepath)) + "; charset=UTF-8");
        res.content_length(size);
        res.keep_alive(request->keep_alive());
        return res;
    }

}  // namespace rdhttp
